﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Cofe.Core;
using Cofe.Core.Filter;
using Cofe.Core.Interfaces;
using Cofe.Core.Logger;
using Cofe.Core.Property;
using Cofe.Core.Service;
using Cofe.Core.Session;
using Cofe.Core.Utils;

namespace Cofe.Data
{
    public class EFDatabaseUpdater : IActiveDatabaseUpdater
    {

        public void AbortActiveUpdate()
        {
            _cts.Cancel();
        }

        public void StartActiveUpdate()
        {
            var repositoryFactory = CofeServices.ServiceLocater.FindService<IRepositoryFactory>();

            CofeServices.TaskManager.StartRepeat((token) =>
                findExpiredEntryAndUpdate(repositoryFactory, token), TimeSpan.FromSeconds(2),
                _cts.Token, _totalUpdateEntryThreads);
        }

        private static CancellationTokenSource _cts = new CancellationTokenSource();
        private static List<Guid> _updatingIds = new List<Guid>();        
        public static int _totalUpdateEntryThreads = 2;

        private static Queue<DatabaseUpdateEntry> _expiredQueue = new Queue<DatabaseUpdateEntry>();

        private List<DatabaseUpdateEntry> findExpired(IRepositoryFactory repositoryFactory, UpdateType updateType, int count = 5)
        {
            lock (_expiredQueue)
            {
                //update the expireQueue, find all Expired
                if (_expiredQueue.Count == 0)
                    using (var r = repositoryFactory.CreateRepository())
                    {
                        foreach (var de in r.FindExpired(updateType, -1))
                            _expiredQueue.Enqueue(de);
                    }

                List<DatabaseUpdateEntry> retList = new List<DatabaseUpdateEntry>();
                for (int i = 0; i < count; i++)
                    if (_expiredQueue.Any())
                        retList.Add(_expiredQueue.Dequeue());
                return retList;
            }
        }

        private void updateEntry(IRepositoryFactory repositoryFactory, IEnumerable<DatabaseUpdateEntry> updateList, CancellationToken token)
        {
            var parsedList = from de in updateList
                             select new
                             {
                                 DatabaseEntry = de,
                                 PropertyHost = CofeServices.PathParserManager.Behaviors.TryParsePath(de.ParseName,
                                 ParsableType.ParsePathLookup, EntryConstructionFlags.DefaultWithoutCache)
                             };
            using (var r = repositoryFactory.CreateRepository())
            {
                foreach (var nonExistEntry in parsedList.Where(e => e.PropertyHost == null))
                    r.Remove(nonExistEntry.DatabaseEntry.Id);

                var listingExpiredEntries = parsedList.Where(e => e.PropertyHost != null && e.DatabaseEntry.ListingExpired).ToArray();
                var entryExpiredEntries = parsedList.Where(e => e.PropertyHost != null && e.DatabaseEntry.EntryExpired).ToArray();

                r.AddOrUpdate(false, false, entryExpiredEntries.Select(p => p.PropertyHost).ToArray());
                foreach (var dph in listingExpiredEntries)
                    r.RefreshListing(dph.PropertyHost);

                r.SaveChanges();
            }
        }

        private void findExpiredEntryAndUpdate(IRepositoryFactory repositoryFactory, CancellationToken token)
        {
            using (var ctx = CofeServices.SecurityManager
               .ImpersonateAsService(SecurityConstants.ServiceType_ActiveUpdater))
            {
                while (!(token.IsCancellationRequested))
                {
                    try
                    {
                        var expiredEntry = findExpired(repositoryFactory, UpdateType.updateExpired | UpdateType.updateListing, 5)
                            .Where(e => e.ParseName != "{EntryTypes}");
                        if (expiredEntry.Count() > 0)
                            updateEntry(repositoryFactory, expiredEntry, token);
                        else Task.Delay(TimeSpan.FromSeconds(new Random().Next(30) + 5)).Wait();
                    }
                    catch (Exception ex)
                    {
                        CofeServices.Logger.Log(ex);
                    }
                }
            }
        }


    }
}
